using Agron.Data.Contracts;
using Agron.Services.Proxies;

using System;
using System.Collections.Generic;
using System.Security;
using System.Security.Principal;
using System.Threading;


namespace Agron.Security.ClientLayer
{
    /// <summary>
    /// Implementation of <see cref="IPrincipal"/>.
    /// </summary>
    public class AgronPrincipal : IPrincipal
    {
        private readonly AgronIdentity _identity;
        private readonly List<string> _roleList;

        /// <summary>
        /// Initializes a new instance of the <see cref="AgronPrincipal"/> class.
        /// </summary>
        /// <param name="identity">The <see cref="AgronIdentity"/> to use as the principal's identity.</param>
        /// <param name="roles">The collection whose elements are copied to the role list.</param>
        /// <remarks>Locked constructor. The client must use <see cref="AgronPrincipal.Initialize"/> to get an instance of the class.</remarks>
        private AgronPrincipal(AgronIdentity identity, IEnumerable<string> roles)
        {
            this._identity = identity;
            this._roleList = new List<string>(roles);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AgronPrincipal"/> class for a specified username.
        /// </summary>
        /// <returns>a new instance of the <see cref="AgronPrincipal"/> class.</returns>
        public static AgronPrincipal Initialize(string username)
        {
            using (MembershipServiceProxy membershipService = new MembershipServiceProxy())
            {
                UserProfile userProfile = membershipService.GetUserProfileByUserName(username);
                if (null != userProfile)
                {
                    AgronIdentity identity = new AgronIdentity(userProfile);
                    AgronPrincipal principal = new AgronPrincipal(identity, new string[0]);

                    AppDomain.CurrentDomain.SetThreadPrincipal(principal);
                    Thread.CurrentPrincipal = principal;

                    return principal;
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        }

        /// <summary>
        /// Gets the current instance of <see cref="AgronPrincipal"/> extracted from the <see cref="Thread.CurrentPrincipal"/>
        /// or throws a <see cref="SecurityException"/> if an <see cref="AgronPrincipal"/> has not been initialized.
        /// </summary>
        /// <value>The current instance of <see cref="AgronPrincipal"/> extracted from the <see cref="Thread.CurrentPrincipal"/>.</value>
        public static AgronPrincipal Current
        {
            get
            {
                AgronPrincipal principal = (Thread.CurrentPrincipal as AgronPrincipal);
                if (null == principal)
                    throw new SecurityException();

                return principal;
            }
        }

        /// <summary>
        /// Gets the <see cref="AgronIdentity"/> of the current principal.
        /// </summary>
        /// <value></value>
        /// <returns>The <see cref="AgronIdentity"/> object associated with the current principal.</returns>
        public AgronIdentity AgronIdentity { get { return this._identity; } }

        #region IPrincipal Members

        /// <summary>
        /// Gets the identity of the current principal.
        /// </summary>
        /// <value></value>
        /// <returns>The <see cref="T:System.Security.Principal.IIdentity"></see> object associated with the current principal.</returns>
        public IIdentity Identity { get { return this._identity; } }

        /// <summary>
        /// Determines whether the current principal belongs to the specified role.
        /// </summary>
        /// <param name="role">The name of the role for which to check membership.</param>
        /// <returns>
        /// true if the current principal is a member of the specified role; otherwise, false.
        /// </returns>
        public bool IsInRole(string role)
        {
            return this._roleList.Contains(role);
        }

        #endregion
    }
}
